package com.ruben.simplescaffold.utils.sensitive;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 初始化敏感词库，将敏感词加入到HashMap中，构建DFA算法模型
 *
 * @author : chenming
 * @since 2014年4月20日 下午2:27:06
 */
public class SensitiveWordInit {
    /**
     * volatile避免指令重排
     */
    private volatile static SensitiveWordInit lazyMan;
    /**
     * 红绿等解决通过反射创建对象（反编译可以破解该方法）
     */
    private static boolean SINGLE_SIGN = false;
    @SuppressWarnings("rawtypes")
    public HashMap sensitiveWordMap;

    private SensitiveWordInit() {
        synchronized (SensitiveWordInit.class) {
            if (!SINGLE_SIGN) {
                SINGLE_SIGN = true;
            } else {
                throw new RuntimeException("不要试图使用反射破坏单例");
            }
        }
    }

    /**
     * 双重检测锁模式的  懒汉式单例    DCL懒汉式
     *
     * @return
     */
    public static SensitiveWordInit getInstance() {
        if (lazyMan == null) {
            // 不是一个原子性操作
            lazyMan = new SensitiveWordInit();
        }
        return lazyMan;
    }

    /**
     * @author chenming
     * @since 2014年4月20日 下午2:28:32
     */
    @SuppressWarnings("rawtypes")
    public Map initKeyWord(String filePath) {
        try {
            // 读取敏感词库
            Set<String> keyWordSet = readSensitiveWordFile(filePath);
            // 将敏感词库加入到HashMap中
            addSensitiveWordToHashMap(keyWordSet);
            // spring获取application，然后 application setAttribute( "sensitiveWordMap", sensitiveWordMap );
        } catch (Exception e) {
            e.printStackTrace();
            return Collections.emptyMap();
        }
        return sensitiveWordMap;
    }

    /**
     * 读取敏感词库，将敏感词放入HashSet中，构建一个DFA算法模型：<br>
     * 中 = {
     * isEnd = 0
     * 国 = {<br>
     * isEnd = 1
     * 人 = {isEnd = 0
     * 民 = {isEnd = 1}
     * }
     * 男  = {
     * isEnd = 0
     * 人 = {
     * isEnd = 1
     * }
     * }
     * }
     * }
     * 五 = {
     * isEnd = 0
     * 星 = {
     * isEnd = 0
     * 红 = {
     * isEnd = 0
     * 旗 = {
     * isEnd = 1
     * }
     * }
     * }
     * }
     *
     * @param keyWordSet 敏感词库
     * @author chenming
     * @since 2014年4月20日 下午3:04:20
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    private void addSensitiveWordToHashMap(Set<String> keyWordSet) {
        // 初始化敏感词容器，减少扩容操作
        sensitiveWordMap = new HashMap(keyWordSet.size());
        String key;
        Map nowMap;
        Map<String, String> newWorMap;
        // 迭代keyWordSet
        for (String s : keyWordSet) {
            // 关键字
            key = s;
            nowMap = sensitiveWordMap;
            for (int i = 0; i < key.length(); i++) {
                // 转换成char型
                char keyChar = key.charAt(i);
                // 获取
                Object wordMap = nowMap.get(keyChar);
                // 如果存在该key，直接赋值
                if (wordMap != null) {
                    nowMap = (Map) wordMap;
                } else {
                    // 不存在则，则构建一个map，同时将isEnd设置为0，因为他不是最后一个
                    newWorMap = new HashMap<>(1 << 2);
                    // 不是最后一个
                    newWorMap.put("isEnd", "0");
                    nowMap.put(keyChar, newWorMap);
                    nowMap = newWorMap;
                }

                if (i == key.length() - 1) {
                    // 最后一个
                    nowMap.put("isEnd", "1");
                }
            }
        }
    }

    /**
     * 读取敏感词库中的内容，将内容添加到set集合中
     *
     * @throws FileNotFoundException,NullPointerException 抛出空指针或者文件未找到异常
     * @author chenming
     * @since 2014年4月20日 下午2:31:18
     */
    private Set<String> readSensitiveWordFile(String filePath) throws Exception {
        Set<String> set;
        InputStream inputStream;
        try {
            inputStream = new FileInputStream(filePath);
        } catch (FileNotFoundException e) {
            inputStream = Objects.requireNonNull(ClassLoader.getSystemResourceAsStream("sensitive.txt"));
        }
        try (InputStreamReader read = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
            set = new HashSet<>();
            BufferedReader bufferedReader = new BufferedReader(read);
            String txt;
            // 读取文件，将文件内容放入到set中
            while ((txt = bufferedReader.readLine()) != null) {
                set.add(txt);
            }
        }
        //关闭文件流
        return set;
    }
}
